package com.mjk.common.tools.xml;

import com.mjk.common.base.constns.BeeCommonConstns;
import com.mjk.common.tools.xml.conver.BeeXmlConverType;
import com.mjk.common.tools.xml.conver.impl.LinkedTreeMapConverter;
import com.mjk.common.util.ClassUtil;
import com.mjk.common.util.ObjectUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.basic.DateConverter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import com.thoughtworks.xstream.security.AnyTypePermission;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Xstream工厂类 用于获取xstream实例
 */
public class BeeXmlFactory {
    /**
     * 用于记录xstream
     */
    private static final Map<String, XStream> xStreamMap = new ConcurrentHashMap<>();
    // 编码格式
    private static final String ENCODING = "GBK";
    //时区
    private static final String CHINA_TIME_ZONE = "Asia/Shanghai";
    private static final DomDriver fixDriver = new DomDriver(ENCODING, new XmlFriendlyNameCoder("_-", "_"));
    //private static Set<Class<?>> clazzs;

    static {
       // clazzs = ClassUtil.scanPackageByAnnotation(BeeCommonConstns.BASE_PACKAGE, false, XStreamAlias.class);
    }

    /**
     * 获取xstream实例，对于相同的class返回同一个xstream实例 不是每次都返回一个新的
     * 规避 OOM 问题
     *
     * @param clazz
     * @return
     */
    public static XStream getXStream(Class<?> clazz) {
        return getXStream(clazz, false,null);
    }

    /**
     * 指定某些类型的别名 特别是像Map List之类的
     * @param clazz
     * @param alias
     * @return
     */
    public static XStream getXStream(Class<?> clazz,Map<String,Class<?>> alias) {
       return getXStream(clazz, false,alias);
    }

    /**
     *
     * @param clazz
     * @param isNull
     * @return
     */
    public static XStream getXStream(Class<?> clazz,boolean isNull) {
        return getXStream(clazz, isNull,null);
    }
    /**
     * 是否返回 null字段
     *
     * @param clazz
     * @param isNull
     * @return
     */
    public static XStream getXStream(Class<?> clazz, boolean isNull,Map<String,Class<?>> alias) {
        XStream xStream = xStreamMap.get(getXStreamName(clazz, isNull));
        if (xStream == null) {
            xStream = createXStream(clazz);
            List<Converter> converterList = BeeXmlConverType.getAllConverter(isNull);
            for (Converter converter : converterList) {
                xStream.registerConverter(converter);
            }
            if(!CollectionUtils.isEmpty(alias)){
                for(Map.Entry<String,Class<?>> entry : alias.entrySet()){
                    xStream.alias(entry.getKey(),entry.getValue());
                }
            }
            xStreamMap.put(getXStreamName(clazz, isNull), xStream);
        }
        return xStream;
    }

    private static XStream createXStream(Class<?> clazz) {
        XStream xStream = new XStream(fixDriver) {
            @Override
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return new MapperWrapper(next) {
                    @Override
                    public boolean shouldSerializeMember(Class definedIn, String fieldName) {
                        if (definedIn == Object.class) {
                            try {
                                return this.realClass(fieldName) != null;
                            } catch (Exception e) {
                                return false;
                            }
                        } else {
                            return super.shouldSerializeMember(definedIn, fieldName);
                        }
                    }
                };
            }
        };
        TimeZone zone = TimeZone.getTimeZone(CHINA_TIME_ZONE); //获得时区
        xStream.registerConverter(new DateConverter(zone), XStream.PRIORITY_NORMAL);
        xStream.autodetectAnnotations(true); //开启序列化的注解形式
        xStream.processAnnotations(clazz);
        //如果生成的是Objec不显示class属性
        xStream.aliasSystemAttribute(null, "class");
        xStream.addPermission(AnyTypePermission.ANY);
        xStream.setMode(XStream.NO_REFERENCES);
        addImplicit(xStream,clazz);
        return xStream;
    }

    /**
     * 添加 list的属性名称
     * @param xStream
     * @param clazz
     */
    private static void addImplicit(XStream xStream,Class clazz){
        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            if(ClassUtil.isBasicType(f.getType())){
                addImplicit(xStream,f.getType());
            }
            XStreamImplicit implicit = f.getAnnotation(XStreamImplicit.class);
            if (null != implicit) {
                xStream.addImplicitCollection(clazz, f.getName(), implicit.itemFieldName(), f.getType());
            }
        }

    }


    private static String getXStreamName(Class<?> clazz, boolean isNull) {
        if (isNull) {
            return "true" + clazz.getName();
        }
        return clazz.getSimpleName();
    }

}
